#pragma once
#include<iostream>
#include<queue>
#include"Cond.hpp"

namespace BlockQueueModule
{
    using namespace CondModule;
    using namespace MutexModule;
    template<typename T>
    class BlockQueue
    {
    private:
        bool IsFull()
        {
            return _q.size() == _cap;
        }
        bool Empty()
        {
            return _q.empty();
        }
    public:
        BlockQueue(int cap)
        :_cap(cap)
        ,_p_wait_num(0)
        ,_c_wait_num(0)
        {}
        ~BlockQueue()
        {}

        void Equeue(const T& in)
        {
            MutexGuard lock(_mutex);
            while(IsFull())
            {
                _p_wait_num++;
                _p_cond.Wait(_mutex);
                _p_wait_num--;
            }
            _q.push(in);

            if(_c_wait_num != 0)
            {
                _c_cond.Signal();
            }
        }

        void Pop(T* out)
        {
            MutexGuard lock(_mutex);
            while(Empty())
            {
                _c_wait_num++;
                _c_cond.Wait(_mutex);
                _c_wait_num--;
            }

            *out = _q.front();
            _q.pop();

            if(_p_wait_num > 0)
            {
                _p_cond.Signal();
            }
        }
    private:
        std::queue<T> _q;
        int _cap;
        Cond _c_cond;
        Cond _p_cond;
        Mutex _mutex;
        int _c_wait_num;
        int _p_wait_num;
    };
}